#pragma once
#include "util.hpp"
#include "logmessage.hpp"
#include "formatitem.hpp"
#include <memory>
#include <mutex>
#include <iostream>
#include <fstream>
#include <sstream>
#include <ctime>
using std::cout;
using std::ofstream;
using std::shared_ptr;
using std::string;
using std::stringstream;

namespace mylog
{
    class LogSink
    {
    public:
        using ptr = std::shared_ptr<LogSink>;
        LogSink() {};
        ~LogSink() {};
        virtual void Log(const char *data, size_t len) = 0;
    };
    class StdoutSink : public LogSink // 单纯向屏幕落地的类
    {
    public:
        using ptr = std::shared_ptr<StdoutSink>;
        StdoutSink() = default;
        void Log(const char *data, size_t len)
        {
            cout.write(data, len);
        }
    };
    class FileSink : public LogSink // 向文件落地的类
    {
    public:
        FileSink(const string &pathname) : _pathname(pathname)
        {
            // 首先要保证文件目录存在
            util::File::CreateDirectory(util::File::GetPath(_pathname));
            // 然后保证文件要能创建
            _ofs.open(pathname, std::ios::binary | std::ios::app);
            if (_ofs.is_open() == false)
            {
                assert("文件落地类打开文件失败！");
            }
        }
        void Log(const char *data, size_t len)
        {
            _ofs.write(data, len);
            if (_ofs.good() == false)
            {
                assert("日志输出文件失败！");
            }
        }

    private:
        string _pathname;
        ofstream _ofs;
    };

    class RollBySizeSink : public LogSink
    {
    public:
        RollBySizeSink(const string &base_name, size_t max_size) : _basename(base_name), _max_size(max_size), _cur_size(0)
        {
            // 首先要保证文件目录存在
            util::File::CreateDirectory(util::File::GetPath(_basename));
        }
        void Log(const char *data, size_t len)
        {
            InitLogFile();
            _ofs.write(data, len);
            if (_ofs.good() == false)
            {
                assert("日志文件输出失败！");
            }
            _cur_size += len;
        }

    private:
        void InitLogFile()
        {
            if (_ofs.is_open() == false || _cur_size >= _max_size)
            {
                _ofs.close();
                string name = CreateFileName();
                _ofs.open(name, std::ios::app | std::ios::binary);
                assert(_ofs.is_open());
                _cur_size = 0;
                return;
            }
            return;
        }
        string CreateFileName()
        {
            stringstream ss;
            struct tm lt;
            time_t t = util::Time::GetTime();
            localtime_r(&t, &lt);
            ss << _basename;
            ss << lt.tm_year + 1900;
            ss << lt.tm_mon + 1;
            ss << lt.tm_mday;
            ss << lt.tm_hour;
            ss << lt.tm_min;
            ss << lt.tm_sec;
            ss << ".log";
            return ss.str();
        }

    private:
        string _basename;
        size_t _max_size;
        size_t _cur_size;
        ofstream _ofs;
    };

    class SinkFactory
    {
    public:
        template <typename SinkType, typename... Args>
        static LogSink::ptr Create(Args &&...args)
        {
            return std::make_shared<SinkType>(std::forward<Args>(args)...);
        }
    };
};